package io.gitee.zhangbinhub.admin.oauth.service

import com.mybatisflex.core.query.QueryChain
import com.mybatisflex.core.query.QueryWrapper
import com.mybatisflex.core.table.TableInfoFactory
import com.mybatisflex.core.update.UpdateChain
import io.gitee.zhangbinhub.acp.boot.exceptions.WebException
import io.gitee.zhangbinhub.acp.cloud.resource.server.tools.TokenTools
import io.gitee.zhangbinhub.acp.core.common.CommonTools
import io.gitee.zhangbinhub.admin.common.vo.CustomerQueryPageVo
import io.gitee.zhangbinhub.admin.oauth.base.OauthBaseService
import io.gitee.zhangbinhub.admin.oauth.component.AuthPasswordEncrypt
import io.gitee.zhangbinhub.admin.oauth.constant.OauthConstant
import io.gitee.zhangbinhub.admin.oauth.entity.*
import io.gitee.zhangbinhub.admin.oauth.po.UserPo
import io.gitee.zhangbinhub.admin.oauth.po.UserQueryPo
import io.gitee.zhangbinhub.admin.oauth.repo.*
import io.gitee.zhangbinhub.admin.resource.server.vo.UserVo
import org.noear.solon.annotation.Component
import org.noear.solon.annotation.Inject
import org.noear.solon.data.annotation.Transaction
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicLong

@Component
open class UserService(
    @Inject userRepository: UserRepository,
    private val applicationRepository: ApplicationRepository,
    private val organizationRepository: OrganizationRepository,
    private val roleRepository: RoleRepository,
    private val userRoleRepository: UserRoleRepository,
    private val userOrgRepository: UserOrgRepository,
    private val userOrgMngRepository: UserOrgMngRepository,
    private val authPasswordEncrypt: AuthPasswordEncrypt
) : OauthBaseService(userRepository) {
    @Throws(WebException::class)
    private fun validatePermit(loginNo: String, userPo: UserPo, roleSet: List<Role>, isCreate: Boolean) {
        val currUserInfo = getUserInfoByLoginNo(loginNo) ?: throw WebException("无法获取当前用户信息")
        if (!isSuper(currUserInfo)) {
            if (currUserInfo.levels >= userPo.levels!!) {
                throw WebException("不能编辑级别比自身大的用户信息")
            }
            getAllOrgList(organizationRepository, currUserInfo.organizationMngSet.toMutableList()).map { it.id }.let {
                userPo.orgIds.forEach { orgId ->
                    if (!it.contains(orgId)) {
                        throw WebException("没有权限编辑指定机构下的用户，请联系系统管理员")
                    }
                }
                userPo.orgMngIds.forEach { orgId ->
                    if (!it.contains(orgId)) {
                        throw WebException("没有权限编辑指定机构下的用户，请联系系统管理员")
                    }
                }
            }
            val roleMinLevel = getRoleMinLevel(currUserInfo)
            roleSet.forEach {
                if (!roleMinLevel.containsKey(it.appId) || roleMinLevel.getValue(it.appId) >= it.levels) {
                    throw WebException("没有权限编辑角色【${it.name}】，请联系系统管理员")
                }
            }
        } else {
            if (isCreate) {
                if (currUserInfo.levels >= userPo.levels!!) {
                    throw WebException("不能创建级别比自身大的用户")
                }
            }
        }
    }

    private fun doSave(user: User, userPo: UserPo): User =
        doSaveUser(
            user.copy(
                mobile = userPo.mobile,
                name = userPo.name!!,
                enabled = userPo.enabled!!,
                levels = userPo.levels!!,
                sort = userPo.sort,
                organizationSet = if (userPo.orgIds.isNotEmpty()) {
                    organizationRepository.selectListByIds(userPo.orgIds)
                } else {
                    mutableListOf()
                },
                organizationMngSet = if (userPo.orgMngIds.isNotEmpty()) {
                    organizationRepository.selectListByIds(userPo.orgMngIds)
                } else {
                    mutableListOf()
                }
            )
        )

    @Transaction
    open fun doSaveUser(user: User): User {
        userRepository.insertOrUpdateSelective(user)
        userRoleRepository.deleteByQuery(QueryWrapper.create().where(UserRole::userId).eq(user.id))
        userOrgRepository.deleteByQuery(QueryWrapper.create().where(UserOrg::userId).eq(user.id))
        userOrgMngRepository.deleteByQuery(QueryWrapper.create().where(UserOrgMng::userId).eq(user.id))
        if (user.roleSet.isNotEmpty()) {
            userRoleRepository.insertBatch(user.roleSet.map { role ->
                UserRole(userId = user.id, roleId = role.id)
            })
        }
        if (user.organizationSet.isNotEmpty()) {
            userOrgRepository.insertBatch(user.organizationSet.map { org ->
                UserOrg(userId = user.id, organizationId = org.id)
            })
        }
        if (user.organizationMngSet.isNotEmpty()) {
            userOrgMngRepository.insertBatch(user.organizationMngSet.map { org ->
                UserOrgMng(userId = user.id, organizationId = org.id)
            })
        }
        if (!user.enabled) {
            removeToken(user.loginNo)
        }
        return user
    }

    @Throws(WebException::class)
    fun findModifiableUserList(loginNo: String): MutableList<UserVo> {
        val user = getUserInfoByLoginNo(loginNo) ?: throw WebException("无法获取当前用户信息")
        return (if (isSuper(user)) {
            userRepository.selectAll()
        } else {
            user.let {
                userRepository.selectListByQuery(QueryWrapper.create().where(User::levels).gt(it.levels))
            }
        }).map { item -> formatUserVo(item) }.toMutableList()
    }

    @Transaction
    @Throws(WebException::class)
    open fun doCreate(loginNo: String, userPo: UserPo): User {
        val roleSet = if (userPo.roleIds.isNotEmpty()) {
            roleRepository.selectListByIds(userPo.roleIds)
        } else {
            mutableListOf()
        }
        validatePermit(loginNo, userPo, roleSet, true)
        val checkUser = userRepository.selectOneByQuery(
            QueryWrapper.create().where(User::loginNo).eq(userPo.loginNo)
        )
        if (checkUser != null) {
            throw WebException("登录账号已存在，请重新输入")
        }
        return doSave(
            User(
                loginNo = userPo.loginNo!!,
                password = authPasswordEncrypt.encrypt(userPo.loginNo!!, OauthConstant.defaultPassword),
                roleSet = roleSet
            ), userPo
        )
    }

    @Transaction
    @Throws(WebException::class)
    open fun doUpdate(loginNo: String, userPo: UserPo): User {
        val roleSet = if (userPo.roleIds.isNotEmpty()) {
            roleRepository.selectListByIds(userPo.roleIds)
        } else {
            mutableListOf()
        }
        validatePermit(loginNo, userPo, roleSet, false)
        return doSave(userRepository.selectOneById(userPo.id!!).apply {
            val checkUser = userRepository.selectOneByQuery(
                QueryWrapper.create().where(User::loginNo).eq(userPo.loginNo)
                    .and(User::id).ne(this.id)
            )
            if (checkUser != null) {
                throw WebException("登录账号已存在，请重新输入")
            }
            if (this.loginNo != userPo.loginNo) {
                this.loginNo = userPo.loginNo!!
                this.password = authPasswordEncrypt.encrypt(userPo.loginNo!!, OauthConstant.defaultPassword)
                this.lastUpdatePasswordTime = null
                removeToken(userPo.loginNo!!)
            }
            this.roleSet = roleSet
        }, userPo)
    }

    @Transaction
    @Throws(WebException::class)
    open fun doUpdatePwd(loginNo: String, userId: String): User =
        userRepository.selectOneById(userId).apply {
            (getUserInfoByLoginNo(loginNo) ?: throw WebException("找不到当前用户信息")).let {
                if (!isSuper(it)) {
                    if (it.levels >= this.levels) {
                        throw WebException("不能修改级别比自身大或相等的用户信息")
                    }
                }
                this.password = authPasswordEncrypt.encrypt(this.loginNo, OauthConstant.defaultPassword)
                this.lastUpdatePasswordTime = null
                userRepository.update(this, false)
                removeToken(this.loginNo)
            }
        }

    @Transaction
    @Throws(WebException::class)
    open fun doDelete(loginNo: String, idList: MutableList<String>) {
        val user = getUserInfoByLoginNo(loginNo) ?: throw WebException("找不到当前用户信息")
        if (idList.contains(user.id)) {
            throw WebException("不能删除自己")
        }
        val userList = userRepository.selectListByIds(idList)
        if (!isSuper(user)) {
            userList.forEach {
                if (user.levels >= it.levels) {
                    throw WebException("没有权限做此操作，请联系系统管理员")
                }
            }
        }
        userRoleRepository.deleteByQuery(QueryWrapper.create().where(UserRole::userId).`in`(idList))
        userOrgRepository.deleteByQuery(QueryWrapper.create().where(UserOrg::userId).`in`(idList))
        userOrgMngRepository.deleteByQuery(QueryWrapper.create().where(UserOrgMng::userId).`in`(idList))
        userRepository.deleteBatchByIds(idList)
        userList.forEach { item -> removeToken(item.loginNo) }
    }

    @Transaction
    @Throws(WebException::class)
    open fun disableUser(loginNo: String) = getUserInfoByLoginNo(loginNo)?.apply {
        UpdateChain.of(userRepository).set(User::enabled, false).where(User::id).eq(this.id)
    } ?: throw WebException("找不到用户信息【$loginNo】")

    private fun removeToken(loginNo: String) {
        applicationRepository.selectAll()
            .forEach { application -> TokenTools.revokeAccessToken(application.id, loginNo) }
    }

    fun doQuery(userQueryPo: UserQueryPo): CustomerQueryPageVo<UserVo> =
        QueryChain.of(userRepository)
            .select(*(TableInfoFactory.ofEntityClass(User::class.java).allColumns))
            .from(User::class.java)
            .where(User::loginNo).like(userQueryPo.loginNo, !CommonTools.isNullStr(userQueryPo.loginNo))
            .and(User::name).like(userQueryPo.name, !CommonTools.isNullStr(userQueryPo.name))
            .and(User::enabled).eq(userQueryPo.enabled, userQueryPo.enabled != null)
            .and(User::id).`in`(
                QueryChain.of(userOrgRepository)
                    .select(UserOrg::userId)
                    .from(UserOrg::class.java).`as`("uo")
                    .leftJoin(Organization::class.java).`as`("o").on(UserOrg::organizationId, Organization::id)
                    .where(Organization::name).like(userQueryPo.orgName),
                !CommonTools.isNullStr(userQueryPo.orgName)
            )
            .and(User::id).`in`(
                QueryChain.of(userRoleRepository)
                    .select(UserRole::userId)
                    .from(UserRole::class.java).`as`("ur")
                    .leftJoin(Role::class.java).`as`("r").on(UserRole::roleId, Role::id)
                    .where(Role::name).like(userQueryPo.roleName),
                !CommonTools.isNullStr(userQueryPo.roleName)
            )
            .let { queryChain ->
                doPageQuery(userQueryPo.queryParam!!, queryChain, true)
            }.let { result ->
                CustomerQueryPageVo(
                    content = result.content.map { formatUserVo(it) },
                    totalElements = result.totalElements,
                    currPage = result.currPage,
                    pageSize = result.pageSize
                )
            }

    @Throws(WebException::class)
    fun getUserVoById(userId: String): UserVo = getUserInfoById(userId)?.let {
        formatUserVo(it)
    } ?: throw WebException("找不到用户信息")

    @Throws(WebException::class)
    fun getUserVoByLoginNo(loginNo: String): UserVo = getUserInfoByLoginNo(loginNo)?.let {
        formatUserVo(it)
    } ?: throw WebException("找不到用户信息")

    @Throws(WebException::class)
    fun getUserVoList(): MutableList<UserVo> =
        userRepository.selectListWithRelationsByQuery(QueryWrapper.create())
            .map { item -> formatUserVo(item) }.toMutableList()

    /**
     * 根据ID查询用户信息
     */
    @Throws(WebException::class)
    fun getUserVoListByIdList(idList: MutableList<String>): MutableList<UserVo> =
        userRepository.selectListWithRelationsByQuery(QueryWrapper.create().where(User::id).`in`(idList))
            .map { item -> formatUserVo(item) }.toMutableList()

    /**
     * 根据登录号或姓名模糊查询用户
     */
    @Throws(WebException::class)
    fun getUserVoListByLoginNoOrName(loginNoOrName: String, findAll: Boolean): MutableList<UserVo> =
        userRepository.selectListWithRelationsByQuery(
            QueryWrapper.create()
                .where(User::loginNo).like(loginNoOrName)
                .or(User::name).like(loginNoOrName)
        ).filter { item -> findAll || item.enabled }.map { item -> formatUserVo(item) }.toMutableList()

    /**
     * 获取指定部门下所有符合角色编码的用户
     */
    private fun getUserVoListInOrgListByRoleCode(
        organizations: Collection<Organization>,
        roleCode: List<String>
    ): MutableList<UserVo> =
        mutableListOf<UserVo>().apply {
            organizations.forEach { org ->
                this.addAll(org.userSet.filter { user -> user.roleSet.any { role -> roleCode.contains(role.code) } }
                    .map { item -> formatUserVo(item) }
                    .toMutableList())
            }
        }.let {
            getUserVoListDistinct(it)
        }

    /**
     * User集合去重，返回List
     */
    private fun getUserVoListDistinct(users: Collection<UserVo>): MutableList<UserVo> =
        mutableListOf<UserVo>().apply {
            val userIdList = mutableListOf<String>()
            users.forEach { user ->
                if (!userIdList.contains(user.id)) {
                    this.add(user)
                    userIdList.add(user.id!!)
                }
            }
        }

    /**
     * 获取上级部门指定角色的用户
     * @param orgLevelList >0 下级部门，1下一级，2下二级...；=0：本部门；<0 上级部门，-1上一级，-2上二级...
     */
    @Throws(WebException::class)
    fun getUserVoListByRelativeOrgAndRole(
        loginNo: String,
        orgLevelList: List<Int>,
        roleCode: List<String>
    ): MutableList<UserVo> =
        getUserInfoByLoginNo(loginNo)?.let { currUser ->
            val orgList = mutableListOf<Organization>()
            orgLevelList.forEach { orgLevel ->
                when {
                    orgLevel > 0 -> { // 获取下级
                        val tmpOrg = currUser.organizationSet.toMutableList()
                        for (index in 1..orgLevel) {
                            val children = getRelativeOrgList(index, tmpOrg)
                            if (children.isNotEmpty()) {
                                tmpOrg.clear()
                                tmpOrg.addAll(children)
                            } else {
                                tmpOrg.clear()
                                break
                            }
                        }
                        orgList.addAll(tmpOrg)
                    }

                    orgLevel < 0 -> { // 获取上级
                        val tmpOrg = currUser.organizationSet.toMutableList()
                        for (index in orgLevel until 0) {
                            val parent = getRelativeOrgList(index, tmpOrg)
                            if (parent.isNotEmpty()) {
                                tmpOrg.clear()
                                tmpOrg.addAll(parent)
                            } else {
                                tmpOrg.clear()
                                break
                            }
                        }
                        orgList.addAll(tmpOrg)
                    }

                    else -> { // 本部门
                        orgList.addAll(currUser.organizationSet)
                    }
                }
            }
            getUserVoListInOrgListByRoleCode(orgList, roleCode)
        } ?: throw WebException("无法获取当前用户信息")

    fun getUserVoListByOrgCodeAndRole(orgCode: List<String>, roleCode: List<String>): MutableList<UserVo> =
        getUserVoListInOrgListByRoleCode(
            organizationRepository.selectListByQuery(
                QueryWrapper.create()
                    .where(Organization::code).like(orgCode)
                    .or(Organization::name).like(orgCode)
            ), roleCode
        )

    fun getUserVoListByRole(roleCode: List<String>): MutableList<UserVo> =
        getUserVoListDistinct(
            roleRepository.selectListWithRelationsByQuery(QueryWrapper.create().where(Role::code).`in`(roleCode))
                .flatMap { role -> role.userSet }
                .map { item -> formatUserVo(item) }
                .toMutableList())

    /**
     * 获取相对部门集合
     * @param flag 标识，>0下级，<0上级
     * @param orgList 参考部门集合
     * @return 相对部门集合
     */
    private fun getRelativeOrgList(flag: Int, orgList: Collection<Organization>): Collection<Organization> = when {
        flag > 0 -> { // 获取下级
            organizationRepository.selectListByQuery(
                QueryWrapper.create().where(Organization::getParentId).`in`(orgList.map { org -> org.id })
            )
        }

        flag < 0 -> { // 获取上级
            mutableListOf<Organization>().apply {
                orgList.forEach { org ->
                    val parent = organizationRepository.selectOneById(org.parentId)
                    if (parent != null) {
                        this.add(parent)
                    }
                }
            }
        }

        else -> {
            orgList
        }
    }

    /**
     * 记录用户密码错误次数
     */
    fun storePasswordErrorTime(username: String): Long =
        "${OauthConstant.passwordErrorTimeKeyPrefix}$username".let { keyString ->
            if (!passwordErrorTimeMap.containsKey(keyString)) {
                passwordErrorTimeMap[keyString] = AtomicLong(0)
            }
            passwordErrorTimeMap[keyString]!!.incrementAndGet()
        }

    /**
     * 清除用户密码错误次数
     */
    fun clearPasswordErrorTime(username: String) {
        "${OauthConstant.passwordErrorTimeKeyPrefix}$username".let { keyString ->
            passwordErrorTimeMap.remove(keyString)
        }
    }

    companion object {
        private val passwordErrorTimeMap: ConcurrentHashMap<String, AtomicLong> = ConcurrentHashMap()
    }

}
